/*
 * Copyright 2008-2012 Centro Andaluz de Innovación y Tecnologías de la
 * Información y las Comunicaciones (CITIC). All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 *
 * Please contact CITIC - Edificio CITIC, C/ Marie Curie, 6 Parque
 * Tecnológico de Andalucía 29590 - Campanillas, Málaga, Spain
 * or visit www.citic.es, if you need additional information or
 * have any questions.
 */

package es.citic.puedes.youtube.bbdd;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.NoResultException;
import javax.persistence.Persistence;
import javax.persistence.Query;

import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import es.citic.puedes.youtube.YouTubeException;
import es.citic.puedes.youtube.tables.Keyword;
import es.citic.puedes.youtube.tables.Userkeyword;
import es.citic.puedes.youtube.tables.YoutubeUser;

/**
 * 
 * class that contains the methods for getting and close the EntityManager, the
 * interface to access the service database
 * 
 * 
 */
public class YouTubeDataBase {
	// Log instance
	final Logger logger = LoggerFactory.getLogger(YouTubeDataBase.class);
	
	private EntityManagerFactory emf = null;
	private String persistenceUnit = null;
	private Map<String, String> dBproperties;	
	
	/**
	 * Contructor.
	 * 
	 * @param persistenceUnit persistenceUnit persistence unit name
	 */
	public YouTubeDataBase(String persistenceUnit) {
		this(persistenceUnit, Collections.<String,String>emptyMap());
	}
	
	/**
	 * Contructor.
	 * 
	 * @param persistenceUnit persistence unit name
	 * @param properties db connection properties, example: PersistenceUnitProperties.JDBC_DRIVER, "org.hsqldb.jdbcDriver"
	 */
	public YouTubeDataBase(String persistenceUnit, Map<String, String> properties) {
		this.persistenceUnit = persistenceUnit;
		this.dBproperties = properties;
		this.emf = Persistence.createEntityManagerFactory(this.persistenceUnit, this.dBproperties);
		
	}	
	
	public void install() {		
		HashMap<String, Object> installProperties = new HashMap<String, Object>();
		installProperties.put(PersistenceUnitProperties.CLASSLOADER, this.getClass().getClassLoader()); 
		installProperties.put(PersistenceUnitProperties.DDL_GENERATION, "drop-and-create-tables"); 
		EntityManagerFactory emf2 = Persistence.createEntityManagerFactory(this.persistenceUnit, installProperties);
		EntityManager em = emf2.createEntityManager();
		em.close();
		emf2.close();
	}
	
	/**
	 * Closes the EntityManager after a database query is finished
	 * 
	 * @param em            EntityManager object to be closed
	 */
	public void closeEntityManager(EntityManager em) {
		logger.debug("Enter :closeEntityManager");
		if (em != null)
			em.close();
		/*if (this.emf != null)
			this.emf.close();*/
		logger.debug("Exit :closeEntityManager");
	}

	/**
	 * Gets the EntityManager for database queries
	 * Creates database tables
	 * @return em 					the EntityManager
	 * @throws WeatherException     if any error getting the EntityManager appears
	 */
	public EntityManager getEntityManager() throws YouTubeException {
		logger.debug("Enter :getEntityManager()");

		/*try {
			//if (this.emf == null)
				this.emf = Persistence.createEntityManagerFactory(this.persistenceUnit, this.dBproperties);
		} catch (Exception e) {
			throw new YouTubeException("YouTubeException: error getting the EntityManager "	+ e.getMessage());
		}*/
		logger.debug("getEntityManager ends..");

		EntityManager em = this.emf.createEntityManager(this.dBproperties);

		return em;
	}

	/**
	 * Drop and create all tables.
	 */
	public synchronized void cleanEntityManager() {
		
		logger.debug("Enter :cleanEntityManager()");
		
		if (this.emf != null && this.emf.isOpen()) {
			this.emf.close();
		}
		
		HashMap<String, String> properties = new HashMap<String, String>(this.dBproperties);
		properties.put(PersistenceUnitProperties.DDL_GENERATION, PersistenceUnitProperties.DROP_AND_CREATE);
		this.emf = Persistence.createEntityManagerFactory(this.persistenceUnit,properties);
		EntityManager entityManager = this.emf.createEntityManager();
		if (entityManager != null && entityManager.isOpen()) {
			entityManager.close();
			this.emf.close();
		}
		entityManager = null;
		this.emf = null;
		
		logger.debug("Exit :cleanEntityManager()");
		
	}

	/**
	 * checks whether the user is already stored in the database
	 * 
	 * @param em
	 *            EntityManager for queries
	 * @param userId
	 *            PUEDeS user id
	 * @return YoutubeUser object if the user exists or null in other case
	 * @throws YouTubeException
	 *             if any error in the query appears
	 */
	public YoutubeUser selectYouTubeUserQuery(EntityManager em,	Integer userId) throws YouTubeException {
		YoutubeUser newYoutubeUser = null;
		try {
			newYoutubeUser = (YoutubeUser) em.createQuery("SELECT yu FROM YoutubeUser yu WHERE yu.userid = :userid").setParameter("userid", userId).getSingleResult();

		} catch (NoResultException nre) {
			logger.debug("no user stored for this id");
		} catch (Exception e) {
			throw new YouTubeException("error in the query, more than one user was stored with this id");
		}

		return newYoutubeUser;
	}

	/**
	 * checks whether the YouTube username is already stored in the database
	 * 
	 * @param em
	 *            EntityManager for queries
	 * @param userId
	 *            YouTube username
	 * @return true if the username exists or false in other case
	 * @throws YouTubeException
	 *             if any error in the query appears
	 */

	public boolean selectYouTubeUserNameQuery(EntityManager em,	String username) throws YouTubeException {

		YoutubeUser newYoutubeUser = null;
		boolean usernameExists = false;
		try {
			newYoutubeUser = (YoutubeUser) em.createQuery("SELECT yu FROM YoutubeUser yu WHERE yu.username = :username")
					.setParameter("username", username).getSingleResult();

		} catch (NoResultException nre) {
			logger.debug("no user stored for this username");
		} catch (Exception e) {
			throw new YouTubeException(
					"error in the query, more than one user was stored with this username");
		}

		if ((newYoutubeUser != null) && (newYoutubeUser.getUsername() != null)) {
			usernameExists = true;
			logger.debug("username exists ..");
		}

		return usernameExists;
	}

	/**
	 * gets the list of Userkeyword objects (Preference model) for a YouTube
	 * user
	 * 
	 * @param em
	 *            EntityManager
	 * @param userId
	 *            PUEDeS user id
	 * @return an Userkeyword object list
	 * @throws YouTubeException
	 *             if any error in the query appears
	 */
	@SuppressWarnings("unchecked")
	public List<Userkeyword> selectUserKeywordQuery(EntityManager em,
			Integer userId) throws YouTubeException {

		List<Userkeyword> newUserkeywordList = new ArrayList<Userkeyword>();
		try {
			Query userKeywordQuery = em.createQuery(
					"SELECT uk FROM Userkeyword uk WHERE uk.userid = :userId")
					.setParameter("userId", userId);

			newUserkeywordList = userKeywordQuery.getResultList();

		} catch (NoResultException nre) {
			logger.debug("no userKeyword stored for this user id");
		} catch (Exception e) {
			throw new YouTubeException("YouTubeException: " + e.getMessage());
		}

		return newUserkeywordList;
	}

	/**
	 * checks whether the keyword was selected by the current user previously
	 * 
	 * @param em
	 *            EntityManager
	 * @param keywordName
	 *            keyword to search
	 * @return the Keyword object with the keyword itself
	 * @throws YouTubeException
	 *             if any error in the query appears
	 */
	public Keyword selectKeywordQuery(EntityManager em, String keywordName) throws YouTubeException {
		Keyword keyword = new Keyword();
		try {
			Query keywordQuery = em
					.createQuery(
							"SELECT k FROM Keyword k WHERE k.keywordname = :keywordname")
					.setParameter("keywordname", keywordName);

			keyword = (Keyword) keywordQuery.getSingleResult();

		} catch (NoResultException nre) {
			logger.debug("no Keyword stored for this keyword name");
			keyword=null;
		} catch (Exception e) {
			throw new YouTubeException("YouTubeException: " + e.getMessage());
		}

		return keyword;
	}

}
